home *** CD-ROM | disk | FTP | other *** search
- /* smtpcli.c
- * Client routines for Simple Mail Transfer Protocol ala RFC821
- * A.D. Barksdale Garbee II, aka Bdale, N3EUA
- * Copyright 1986 Bdale Garbee, All Rights Reserved.
- * Permission granted for non-commercial copying and use, provided
- * this notice is retained.
- * Modified 14 June 1987 by P. Karn for symbolic target addresses,
- * also rebuilt locking mechanism
- * Limit on max simultaneous sessions, reuse of connections - 12/87 NN2Z
- */
- #include <stdio.h>
- #if (!ATARI_ST || LATTICE) /* DG2KK */
- #include <fcntl.h>
- #endif
- #include "global.h"
- #include "netuser.h"
- #include "mbuf.h"
- #include "timer.h"
- #include "tcp.h"
- #include "smtp.h"
- #include "trace.h"
- #include "cmdparse.h"
-
- extern int16 lport; /* local port placeholder */
- extern int32 resolve();
- static struct timer smtpcli_t;
- int32 gateway;
-
- #ifdef SMTPTRACE
- int16 smtptrace = 0; /* used for trace level */
- int dosmtptrace();
- #endif
-
- int16 smtpmaxcli = MAXSESSIONS; /* the max client connections allowed */
- int16 smtpcli = 0; /* number of client connections
- * currently open */
-
- static struct smtp_cb *cli_session[MAXSESSIONS]; /* queue of client sessions */
-
- int dosmtptick(),dogateway(),dosmtpmaxcli(),mlock(),dotimer();
- void quit(),abort_trans(),rejextjob(),sendit(),del_session(),del_job();
- void rejectjob(),execjobs(),smtp_transaction();
- struct smtp_cb *newcb(),*lookup();
- struct smtp_job *setupjob();
-
- struct cmds smtpcmds[] = {
- "gateway", dogateway, 0, NULLCHAR, NULLCHAR,
- "kick", dosmtptick, 0, NULLCHAR, NULLCHAR,
- "maxclients", dosmtpmaxcli, 0, NULLCHAR, NULLCHAR,
- "timer", dotimer, 0, NULLCHAR, NULLCHAR,
- #ifdef SMTPTRACE
- "trace", dosmtptrace, 0, NULLCHAR, NULLCHAR,
- #endif
- NULLCHAR, NULLFP, 0,
- "subcommands: gateway kick maxclients timer trace",
- NULLCHAR,
- };
-
- dosmtp(argc,argv)
- int argc;
- char *argv[];
- {
- return subcmd(smtpcmds,argc,argv);
- }
-
- static int
- dosmtpmaxcli(argc,argv)
- int argc;
- char *argv[];
- {
- int x;
- if (argc < 2)
- printf("%d\n",smtpmaxcli);
- else {
- x = atoi(argv[1]);
- if (x > MAXSESSIONS)
- printf("max clients must be <= %d\n",MAXSESSIONS);
- else
- smtpmaxcli = x;
- }
- return 0;
- }
-
- static int
- dogateway(argc,argv)
- int argc;
- char *argv[];
- {
- char *inet_ntoa();
- int32 n;
- extern char badhost[];
-
- if(argc < 2){
- printf("%s\n",inet_ntoa(gateway));
- } else if((n = resolve(argv[1])) == 0){
- printf(badhost,argv[1]);
- return 1;
- } else
- gateway = n;
- return 0;
- }
-
- #ifdef SMTPTRACE
- static int
- dosmtptrace(argc,argv)
- int argc;
- char *argv[];
- {
- if (argc < 2)
- printf("%d\n",smtptrace);
- else
- smtptrace = atoi(argv[1]);
- return 0;
- }
- #endif
-
- /* Set outbound spool poll interval */
- static int
- dotimer(argc,argv)
- int argc;
- char *argv[];
- {
- int dosmtptick();
-
- if(argc < 2){
- printf("%d/%d\n",smtpcli_t.start - smtpcli_t.count,
- smtpcli_t.start);
- return 0;
- }
- smtpcli_t.func = (void (*)())dosmtptick;/* what to call on timeout */
- smtpcli_t.arg = NULLCHAR; /* dummy value */
- smtpcli_t.start = atoi(argv[1]); /* set timer duration */
- start_timer(&smtpcli_t); /* and fire it up */
- return 0;
- }
-
- /* this is the routine that gets called every so often to do outgoing mail
- processing */
- int
- dosmtptick()
- {
- register struct smtp_cb *cb;
- char tmpstring[LINELEN], wfilename[13], prefix[9];
- char from[LINELEN], to[LINELEN];
- char *cp, *cp1;
- int32 destaddr;
- FILE *wfile;
-
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("smtp daemon entered\n");
- fflush(stdout);
- }
- #endif
- for(filedir(mailqueue,0,wfilename);wfilename[0] != '\0';
- filedir(mailqueue,1,wfilename)){
-
- /* save the prefix of the file name which it job id */
- cp = wfilename;
- cp1 = prefix;
- while (*cp && *cp != '.')
- *cp1++ = *cp++;
- *cp1 = '\0';
-
- /* lock this file from the smtp daemon */
- if (mlock(mailqdir,prefix))
- continue;
-
- sprintf(tmpstring,"%s%s",mailqdir,wfilename);
- if ((wfile = fopen(tmpstring,"r")) == NULLFILE) {
- /* probably too many open files */
- (void) rmlock(mailqdir,prefix);
- /* continue to next message. The failure
- * may be temporary */
- continue;
- }
-
- fgets(tmpstring,LINELEN,wfile); /* read target host */
- rip(tmpstring);
- fgets(from,LINELEN,wfile); /* read target host */
- rip(from);
- fgets(to,LINELEN,wfile); /* read target user */
- rip(to);
- fclose(wfile);
-
- if ((destaddr = mailroute(tmpstring)) == 0) {
- printf("** smtpcli: Unknown address %s\n",tmpstring);
- fflush(stdout);
- (void) rmlock(mailqdir,prefix);
- continue;
- }
-
- if ((cb = lookup(destaddr)) == NULLCB) {
- /* there are enough processes running already */
- if (smtpcli >= smtpmaxcli) {
- #ifdef SMTPTRACE
- if (smtptrace) {
- printf("smtp daemon: too many processes\n");
- fflush(stdout);
- }
- #endif
-
- (void) rmlock(mailqdir,prefix);
- break;
- }
- if ((cb = newcb()) == NULLCB) {
- (void) rmlock(mailqdir,prefix);
- break;
- }
- cb->ipaddr = destaddr;
- } else {
- /* This system is already is sending mail lets not
- * interfere with its send queue.
- */
- if (cb->state != CLI_IDLE) {
- (void) rmlock(mailqdir,prefix);
- continue;
- }
- }
- #ifdef SMTPTRACE
- if (smtptrace > 1) {
- printf("queue job %s To: %s From: %s\n",prefix,to,from);
- fflush(stdout);
- }
- #endif
-
- if (setupjob(cb,prefix,to,from) == NULLJOB) {
- (void) rmlock(mailqdir,prefix);
- del_session(cb);
- break;
- }
- }
-
- /* start sending that mail */
- execjobs();
-
- /* Restart timer */
- start_timer(&smtpcli_t);
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("smtp daemon done\n");
- fflush(stdout);
- }
- #endif
- }
-
- /* this is the master state machine that handles a single SMTP transaction */
- void
- smtp_transaction(cb)
- struct smtp_cb *cb;
- {
- void smtp_cts();
- char reply;
- int rcode;
-
- #ifdef SMTPTRACE
- if (smtptrace > 7)
- printf("smtp_transaction() enter state=%u\n",cb->state);
- if (smtptrace) {
- printf("%s\n",cb->buf);
- fflush(stdout);
- }
- #endif
- /* Another line follows; ignore this one */
- if(cb->buf[0] == '0' || cb->buf[3] == '-')
- return;
-
- reply = cb->buf[0];
- rcode = atoi(cb->buf);
-
- /* if service shuting down */
- if (rcode == 421) {
- quit(cb);
- return;
- }
-
- switch(cb->state) {
- case CLI_OPEN_STATE:
- if (reply != '2')
- quit(cb);
- else {
- cb->state = CLI_HELO_STATE;
- sendit(cb,"HELO %s\r\n",hostname);
- }
- break;
- case CLI_HELO_STATE:
- if (reply != '2')
- quit(cb);
- else {
- cb->state = CLI_MAIL_STATE;
- /* send both to speed things up */
- sendit(cb,"MAIL FROM:<%s>\r\nRCPT TO:<%s>\r\n",
- cb->jobq->from,cb->jobq->to);
- }
- break;
- case CLI_MAIL_STATE:
- if (reply != '2')
- quit(cb);
- else {
- cb->state = CLI_RCPT_STATE;
- /* the RCPT is sent already */
- }
- break;
- case CLI_RCPT_STATE:
- if (reply == '5') {
- rejectjob(cb);
- abort_trans(cb);
- } else if (reply != '2')
- abort_trans(cb);
- else {
- /* open text file here because it will be too
- * late to abort in the data state.
- */
- /* if this file open fails abort */
- if ((cb->tfile = fopen(cb->tname,"r")) == NULLFILE)
- abort_trans(cb);
- else {
- cb->state = CLI_DATA_STATE;
- sendit(cb,"DATA\r\n");
- }
- }
- break;
- case CLI_DATA_STATE:
- if (reply != '3')
- abort_trans(cb);
- else {
- cb->state = CLI_SEND_STATE;
- /* Kick the data transfer to get it started */
- smtp_cts(cb->tcb,cb->tcb->window - cb->tcb->sndcnt);
- }
- break;
- case CLI_SEND_STATE:
- /* the transmitter upcall routine will advance the
- state pointer on end of file, so we do nada... */
- break;
- case CLI_UNLK_STATE:
- if (reply == '5') {
- rejectjob(cb);
- abort_trans(cb);
- } else if (reply != '2')
- abort_trans(cb);
- else {
- unlink(cb->wname); /* unlink workfile */
- /* close and unlink the textfile */
- if(cb->tfile != NULLFILE) {
- fclose(cb->tfile);
- cb->tfile = NULLFILE;
- }
- unlink(cb->tname);
- abort_trans(cb);
- }
- break;
- case CLI_QUIT_STATE:
- close_tcp(cb->tcb); /* close up connection */
- break;
- }
- }
-
- /* abort the currrent job. Remove the lockfile.
- * If more work exists set up the next job if
- * not then shut down.
- */
- static void
- abort_trans(cb)
- struct smtp_cb *cb;
- {
- if(cb->tfile != NULLFILE) {
- fclose(cb->tfile);
- cb->tfile = NULLFILE;
- }
- (void) rmlock(mailqdir,cb->jobq->jobname);
- if (nextjob(cb)) {
- sendit(cb,"RSET\r\n");
- cb->state = CLI_HELO_STATE;
- } else {
- sendit(cb,"QUIT\r\n"); /* issue a quit command */
- cb->state = CLI_QUIT_STATE;
- }
- }
-
- /* close down link after a failure */
- static void
- quit(cb)
- struct smtp_cb *cb;
- {
- cb->state = CLI_QUIT_STATE;
- sendit(cb,"QUIT\r\n"); /* issue a quit command */
- }
-
- /* smtp receiver upcall routine. fires up the state machine to parse input */
- static
- void
- smtp_rec(tcb,cnt)
- struct tcb *tcb;
- int16 cnt;
- {
- register struct smtp_cb *cb;
- char *inet_ntoa(), c;
- struct mbuf *bp;
-
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("smtp_rec called\n");
- fflush(stdout);
- }
- #endif
- cb = (struct smtp_cb *)tcb->user; /* point to our struct */
- recv_tcp(tcb,&bp,cnt); /* suck up chars from low level routine */
-
- /* Assemble input line in buffer, return if incomplete */
- while(pullup(&bp,&c,1) == 1) {
- switch(c) {
- case '\r': /* strip cr's */
- continue;
- case '\n': /* line is finished, go do it! */
- cb->buf[cb->cnt] = '\0';
- smtp_transaction(cb);
- cb->cnt = 0;
- break;
- default: /* other chars get added to buffer */
- if(cb->cnt != LINELEN-1)
- cb->buf[cb->cnt++] = c;
- break;
- }
- }
- }
-
- /* smtp transmitter ready upcall routine. twiddles cts flag */
- static
- void
- smtp_cts(tcb,cnt)
- struct tcb *tcb;
- int16 cnt;
- {
- register struct smtp_cb *cb;
- struct mbuf *bp;
- char *cp;
- int c;
-
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("smtp_cts called avail %d\n",cnt);
- fflush(stdout);
- }
- #endif
- cb = (struct smtp_cb *)tcb->user; /* point to our struct */
-
- /* don't do anything until/unless we're supposed to be sending */
- if(cb->state != CLI_SEND_STATE)
- return;
-
- if((bp = alloc_mbuf(cnt)) == NULLBUF){
- /* Hard to know what to do here */
- return;
- }
- cp = bp->data;
- while(cnt > 1 && (c = getc(cb->tfile)) != EOF){
- #if (ATARI_ST)
- if (c == '\n') {
- *cp++ = '\r';
- bp->cnt++;
- cnt--;
- }
- #endif
-
- *cp++ = c;
- bp->cnt++;
- cnt--;
- }
- if(bp->cnt != 0)
- send_tcp(tcb,bp);
- else
- free_p(bp);
-
- if(cnt > 1){ /* EOF seen */
- sendit(cb,"\r\n.\r\n");
- cb->state = CLI_UNLK_STATE;
- }
- }
-
- /* smtp state change upcall routine. */
- static
- void
- smtp_state(tcb,old,new)
- struct tcb *tcb;
- char old,new;
- {
- register struct smtp_cb *cb;
- extern char *tcpstates[];
-
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("smtp_state called: %s\n",tcpstates[new]);
- fflush(stdout);
- }
- #endif
- cb = (struct smtp_cb *)tcb->user;
- switch(new) {
- case ESTABLISHED:
- cb->state = CLI_OPEN_STATE; /* shouldn't be needed */
- break;
- case CLOSE_WAIT:
- close_tcp(tcb); /* shut things down */
- break;
- case CLOSED:
- /* if this close was not done by us ie. a RST */
- if (cb->state != CLI_QUIT_STATE) {
- if(cb->tfile != NULLFILE)
- fclose(cb->tfile);
- }
- del_session(cb);
- del_tcp(tcb);
- break;
- }
- }
-
- /* Send message back to server */
- /*VARARGS*/
- static void
- sendit(cb,fmt,arg1,arg2)
- struct smtp_cb *cb;
- char *fmt,*arg1,*arg2;
- {
- struct mbuf *bp,*qdata();
- char tmpstring[256];
-
- #ifdef SMTPTRACE
- if (smtptrace) {
- printf(">>> ");
- printf(fmt,arg1,arg2);
- fflush(stdout);
- }
- #endif
- sprintf(tmpstring,fmt,arg1,arg2);
- bp = qdata(tmpstring,(int16)strlen(tmpstring));
- send_tcp(cb->tcb,bp);
- }
-
- /* create mail lockfile */
- int
- mlock(dir,id)
- char *dir,*id;
- {
- char lockname[LINELEN];
- int fd;
- /* Try to create the lock file in an atomic operation */
- sprintf(lockname,"%s%s.lck",dir,id);
- #if (ATARI_ST && !LATTICE) /* DG2KK */
- if(!access(lockname,0) || (fd = creat(lockname, 0666)) == -1)
- return -1;
- #else
- if((fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0666)) == -1)
- return -1;
- #endif
- close(fd);
- return 0;
- }
-
- /* remove mail lockfile */
- int
- rmlock(dir,id)
- char *dir,*id;
- {
- char lockname[LINELEN];
- sprintf(lockname,"%s%s.lck",dir,id);
- return(unlink(lockname));
- }
-
- /* free the message struct and data */
- static void
- del_session(cb)
- register struct smtp_cb *cb;
- {
- register int i;
- register struct smtp_job *jp,*tp;
-
- if (cb == NULL)
- return;
- for(i=0; i<MAXSESSIONS; i++)
- if(cli_session[i] == cb) {
- cli_session[i] = NULLCB;
- break;
- }
-
- if(cb->wname != NULLCHAR)
- free(cb->wname);
- if(cb->tname != NULLCHAR)
- free(cb->tname);
- for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
- tp = jp->next;
- (void) rmlock(mailqdir,jp->jobname);
- del_job(jp);
- }
- free((char *)cb);
- smtpcli--; /* number of connections active */
- }
-
- void
- del_job(jp)
- register struct smtp_job *jp;
- {
- if(jp->to != NULLCHAR)
- free(jp->to);
- if(jp->from != NULLCHAR)
- free(jp->from);
- free((char *)jp);
- }
-
- /* move a bad job out of the send queue into a holding area */
- static void
- rejectjob(cb)
- struct smtp_cb *cb;
- {
- char dest[LINELEN];
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("smtp job %s rejected\n",cb->wname);
- fflush(stdout);
- }
- #endif
- /* remove job from queue and save for admin */
- sprintf(dest,"%s%s.txt",baddir,cb->jobq->jobname);
- (void) rename(cb->tname,dest);
- strcpy(rindex(dest,'.'),".wrk");
- (void) rename(cb->wname,dest);
- (void) rmlock(mailqdir,cb->jobq->jobname);
- }
-
- /* look to see if a smtp control block exists for this ipaddr */
- static struct smtp_cb *
- lookup(destaddr)
- int32 destaddr;
- {
- register int i;
-
- for(i=0; i<MAXSESSIONS; i++) {
- if (cli_session[i] == NULLCB)
- continue;
- if (cli_session[i]->ipaddr == destaddr)
- return cli_session[i];
- }
- return NULLCB;
- }
-
- /* create a new smtp control block */
- static struct smtp_cb *
- newcb()
- {
- register int i;
- struct smtp_cb *cb;
-
- for(i=0; i<MAXSESSIONS; i++) {
- if(cli_session[i] == NULLCB) {
- cb = (struct smtp_cb *)calloc(1,sizeof(struct smtp_cb));
- if (cb == NULLCB)
- return(NULLCB);
- cb->wname = malloc((unsigned)strlen(mailqdir) + JOBNAME);
- if (cb->wname == NULLCHAR) {
- free((char *)cb);
- return(NULLCB);
- }
- cb->tname = malloc((unsigned)strlen(mailqdir) + JOBNAME);
- if (cb->tname == NULLCHAR) {
- free(cb->wname);
- free((char *)cb);
- return(NULLCB);
- }
- cb->state = CLI_IDLE;
- cli_session[i] = cb;
- smtpcli++; /* number of connections active */
- return(cb);
- }
- }
- return NULLCB;
- }
-
- static void
- execjobs()
- {
- struct socket lsocket, fsocket;
- void smtp_rec(), smtp_cts(), smtp_state();
- register struct smtp_cb *cb;
- int i;
-
- for(i=0; i<MAXSESSIONS; i++) {
- cb = cli_session[i];
- if (cb == NULLCB)
- continue;
- if(cb->state != CLI_IDLE)
- continue;
-
- sprintf(cb->tname,"%s%s.txt",mailqdir,cb->jobq->jobname);
- sprintf(cb->wname,"%s%s.wrk",mailqdir,cb->jobq->jobname);
-
- /* setup the socket */
- fsocket.address = cb->ipaddr;
- fsocket.port = SMTP_PORT;
- lsocket.address = ip_addr; /* our ip address */
- lsocket.port = lport++; /* next unused port */
- #ifdef SMTPTRACE
- if (smtptrace) {
- printf("Trying Connection to %s\n",inet_ntoa(fsocket.address));
- fflush(stdout);
- }
- #endif
-
- /* open smtp connection */
- cb->state = CLI_OPEN_STATE; /* init state placeholder */
- cb->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,tcp_window,
- smtp_rec,smtp_cts,smtp_state,0,(char *)cb);
- cb->tcb->user = (char *)cb; /* Upward pointer */
- }
- }
-
- /* add this job to control block queue */
- struct smtp_job *
- setupjob(cb,id,to,from)
- struct smtp_cb *cb;
- char *id,*to,*from;
- {
- register struct smtp_job *p1,*p2;
-
- p1 = (struct smtp_job *)calloc(1,sizeof(struct smtp_job));
- if (p1 == NULLJOB)
- return NULLJOB;
- p1->to = malloc((unsigned)strlen(to) + 1);
- if (p1->to == NULLCHAR) {
- free((char *)p1);
- return NULLJOB;
- }
- p1->from = malloc((unsigned)strlen(from) + 1);
- if (p1->from == NULLCHAR) {
- free(p1->to);
- free((char *)p1);
- return NULLJOB;
- }
- strcpy(p1->to,to);
- strcpy(p1->from,from);
- strcpy(p1->jobname,id);
- if ((p2 = cb->jobq) == NULLJOB)
- cb->jobq = p1;
- else {
- while(p2->next != NULLJOB)
- p2 = p2->next;
- p2->next = p1;
- }
- return p1;
- }
-
- /* called to advance to the next job */
- static int
- nextjob(cb)
- struct smtp_cb *cb;
- {
- struct smtp_job *jp;
- jp = cb->jobq->next;
- del_job(cb->jobq);
- if (jp == NULLJOB) {
- cb->jobq = NULLJOB;
- return 0;
- }
- cb->jobq = jp;
- sprintf(cb->tname,"%s%s.txt",mailqdir,cb->jobq->jobname);
- sprintf(cb->wname,"%s%s.wrk",mailqdir,cb->jobq->jobname);
- #ifdef SMTPTRACE
- if (smtptrace > 5) {
- printf("sending %s\n",cb->jobq->jobname);
- fflush(stdout);
- }
- #endif
- return 1;
-
- }
-
-
- /* mail routing funtion. For now just used the hosts file */
- int32
- mailroute(dest)
- char *dest;
- {
- int32 destaddr;
-
- /* look up address or use the gateway */
- if ((destaddr = resolve(dest)) == 0)
- if (gateway != 0)
- destaddr = gateway; /* Use the gateway */
- return destaddr;
-
- }
-
-